
/**
 * Copyright (c) 2012 Gary Higham
 * Released under the MIT license
 * https://code.google.com/p/mock-as2/wiki/License
 *
 * Version: v0.1
 */
import mockAS2.IMockAS2;
import mockAS2.Matcher;
import mockAS2.Stub;

class mockAS2.SpyMethod implements IMockAS2 {
	
	private var originalMethod : Function;
	private var callCount : Number;
	private var passedFunctionArgs : FunctionArguments;
	
	private var proxyStubMethod : Stub;
	
	public function SpyMethod(object : Object, method : String) {
		originalMethod = object[method];
		callCount = 0;
		proxyStubMethod = new Stub(object, method, createProxy(proxy));
	}
	
	public function createProxy(handler:Function):Function {
		var callBack:Function = function() : Object {
            var self:Function = arguments.callee;
			return self.handler.apply(self.target, arguments);
		};
		callBack.handler = handler;
		callBack.target = this;
		return callBack;
	}

	private function proxy() : Object {	
		passedFunctionArgs = arguments;
		callCount++;
        return originalMethod.apply(null, passedFunctionArgs);
    }
    
    public function calledOnce() : Boolean {
    	return callCount == 1;	
    }
    
    public function calledTwice() : Boolean {
    	return callCount == 2;
    }
    
    public function getCallCount() : Number {
    	return callCount;
    }
    
    public function calledWithExactArgs() : Boolean {
		if(passedFunctionArgs.length != arguments.length) {
    		return false;
    	}
    	return matchArguments(arguments);
    }
    
    private function matchArguments(testArguments : Array) : Boolean {
    	for(var i : Number = 0; i < testArguments.length; ++i) {
    		if(!matchVariables(testArguments[i], passedFunctionArgs[i])) {
    			return false;
    		}
    	}
    	return true;
    }
    
    private function matchVariables(testVar : Object, actualVar : Object) : Boolean {
    	if(testVar instanceof Matcher) {
    		return Matcher(testVar).performMatch(actualVar);
    	} else {
    		return actualVar === testVar; 
    	}
	}

	public function calledWithArgs() : Boolean {
		if(passedFunctionArgs.length < arguments.length) {
			return false;
		}
		return matchArguments(arguments);
	}

	public function restore() : Void {
		proxyStubMethod.restore();
	}
}
